package compiler.absyn;

import engine.query.Plan;
import engine.query.Scan;
import storage.record.Schema;

public class Term {
	public Expression lexp,rexp;
	public int op;
	public Term(Expression l,Expression r,int op){
		lexp = l;
		rexp = r;
		this.op = op;
	}
	
	public int reductionFactor(Plan p){
		String lhsName, rhsName;
		if (lexp.isFieldName() && rexp.isFieldName()) {
			lhsName = lexp.asFieldName();
			rhsName = rexp.asFieldName();
			return Math.max(p.distinctValues(lhsName),
							p.distinctValues(rhsName));
		}
		if (lexp.isFieldName()) {
			lhsName = lexp.asFieldName();
			return p.distinctValues(lhsName);
		}
		if (rexp.isFieldName()) {
			rhsName = rexp.asFieldName();
			return p.distinctValues(rhsName);
		}
		// otherwise, the term equates constants
		if (lexp.asConstant().equals(rexp.asConstant()))
			return 1;
		else
			return Integer.MAX_VALUE;
	}

	/**
	 * Determines if this term is of the form "F=c"
	 * where F is the specified field and c is some constant.
	 * If so, the method returns that constant.
	 * If not, the method returns null.
	 * @param fldname the name of the field
	 * @return either the constant or null
	 */
	public Constant equatesWithConstant(String fldname) {
		if (lexp.isFieldName() &&
		    lexp.asFieldName().equals(fldname) &&
		    rexp.isConstant())
				return rexp.asConstant();
		else if (rexp.isFieldName() &&
		    rexp.asFieldName().equals(fldname) &&
			lexp.isConstant())
				return lexp.asConstant();
		else
				return null;
	}

	public String equatesWithField(String fldname) {
		if (lexp.isFieldName() &&
		    lexp.asFieldName().equals(fldname) &&
		    rexp.isFieldName())
				return rexp.asFieldName();
		else if (rexp.isFieldName() &&
		    rexp.asFieldName().equals(fldname) &&
			lexp.isFieldName())
				return lexp.asFieldName();
		else
				return null;
	}

	public boolean appliesTo(Schema sch) {
		return lexp.appliesTo(sch) && rexp.appliesTo(sch);
	}

	public boolean isSatisfied(Scan s) {
		Constant lhsval = lexp.evaluate(s);
		Constant rhsval = rexp.evaluate(s);
		if(op == OpExp.EQ){
			return rhsval.equals(lhsval);
		}
		else if(op == OpExp.NEQ){
			return !(rhsval.equals(lhsval));
		}
		else if(op == OpExp.GT){
			if(!(lhsval instanceof IntConstant)
				||! (rhsval instanceof IntConstant)){
				return false;
			}
			else
				return (Integer)lhsval.asJavaVal()>(Integer)rhsval.asJavaVal();
		}
		else if(op == OpExp.LT){
			if(!(lhsval instanceof IntConstant)
				||! (rhsval instanceof IntConstant)){
				return false;
			}
			else
				return (Integer)lhsval.asJavaVal()<(Integer)rhsval.asJavaVal();
		}
		else if(op == OpExp.GE){
			if(!(lhsval instanceof IntConstant)
				||! (rhsval instanceof IntConstant)){
				return false;
			}
			else
				return (Integer)lhsval.asJavaVal()>=(Integer)rhsval.asJavaVal();
		}
		else if(op == OpExp.LE){
			if(!(lhsval instanceof IntConstant)
				||! (rhsval instanceof IntConstant)){
				return false;
			}
			else
				return (Integer)lhsval.asJavaVal()<=(Integer)rhsval.asJavaVal();
		}
		return false;
	}
	public String toString(){
		return lexp.toString()+","+op+","+rexp.toString();
	}
	
}
